﻿namespace Ex03.GarageLogic
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    internal abstract class Vehicle
    {
        #region Consts
        private const float k_MinValueEnergyPercentage = 0f;
        private const float k_MaxValueEnergyPercentage = 100f;
        #endregion

        #region Messages
        private const string k_StringNotAProperEnergyPercentage = "{0} is not an allowed percentage.";
        private const string k_StringExceedsEnergyPercentage = "{0} is not an in the allowed percentage values.";
        private const string k_LicenseNumber = "License number:";
        private const string k_ModelName = "Model name:";
        private const string k_EnergyPercentageLeft = "Amount of energy left (0-100):";
        private const string k_DisplayVehicle = "------ Vehicle General Information ------";
        private const string k_DisplayLicenseNumber = "License number: ";
        private const string k_DisplayModelName = "Model name: ";
        private const string k_DisplayEnergyPercentageLeft = "Amount of energy left: ";
        private const string k_ErrorNotRightTypeOfEngine = @"The type of engine doesn't match the type entered.";
        private const string k_ErrorNotRightTypeOfFuel = @"The type of fuel doesn't match the type of the current car ({0}).";
        #endregion

        #region Data Members
        protected readonly string r_LicenseNumber;
        protected readonly Engine r_Engine;
        protected string m_ModelName;
        protected float m_EnergyPercentageLeft;
        protected List<Wheel> m_WheelsList;
        #endregion

        #region Constructors
        public Vehicle(string i_LicenseNumber, Engine i_Engine, int i_NumOfWheels, float i_MaxPSI)
        {
            r_LicenseNumber = i_LicenseNumber;
            r_Engine = i_Engine;
            m_WheelsList = new List<Wheel>();
            for (uint i = 1; i <= i_NumOfWheels; i++)
            {
                m_WheelsList.Add(new Wheel(i, i_MaxPSI));
            }
        }
        #endregion

        #region Properties
        public string LicenseNumber 
        {
            get { return r_LicenseNumber; }
        }

        public string ModelName 
        {
            get { return m_ModelName; }
            set { m_ModelName = value; }
        }

        public string EnergyPercentageLeft
        {
            get { return m_EnergyPercentageLeft.ToString(); }
            set 
            {
                float floatEnergyPercentageLeft = k_MinValueEnergyPercentage;
                bool goodInput = false;
                goodInput = float.TryParse(value, out floatEnergyPercentageLeft);

                if (goodInput == false)
                {
                    throw new FormatException(string.Format(k_StringNotAProperEnergyPercentage, value));
                }
                else if (floatEnergyPercentageLeft < k_MinValueEnergyPercentage || floatEnergyPercentageLeft > k_MaxValueEnergyPercentage)
                {
                    throw new ValueOutOfRangeException(string.Format(k_StringExceedsEnergyPercentage, value), k_MinValueEnergyPercentage, k_MaxValueEnergyPercentage);
                }
                else
                {
                    m_EnergyPercentageLeft = floatEnergyPercentageLeft;
                }
            }
        }

        public Engine Engine 
        {
            get { return r_Engine; }
        }

        public bool IsRechargeable
        {
            get
            {
                ElectricEngine electric = Engine as ElectricEngine;
                return electric != null ? true : false;
            }
        }

        public bool IsFuelEngine
        {
            get
            {
                FuelEngine fuel = Engine as FuelEngine;
                return fuel != null ? true : false;
            }
        }
        #endregion

        #region Public Methods

        public virtual void GetAllDetails(ref List<string> io_ListOfVehicleDetails)
        {
            io_ListOfVehicleDetails.Add(GarageLogic.s_EmptyLine);
            io_ListOfVehicleDetails.Add(k_DisplayVehicle);
            io_ListOfVehicleDetails.Add(string.Format(GarageLogic.s_PrintParameter, k_DisplayLicenseNumber, r_LicenseNumber));
            io_ListOfVehicleDetails.Add(string.Format(GarageLogic.s_PrintParameter, k_DisplayModelName, m_ModelName));
            io_ListOfVehicleDetails.Add(string.Format(GarageLogic.s_PrintParameter, k_DisplayEnergyPercentageLeft, EnergyPercentageLeft + "%"));
        }

        public virtual void GetEngineAndWheelsDetails(ref List<string> io_ListOfVehicleDetails)
        {
            r_Engine.GetAllDetails(ref io_ListOfVehicleDetails);

            foreach (Wheel wheel in m_WheelsList)
            {
                wheel.GetAllDetails(ref io_ListOfVehicleDetails);
            }
        }

        public void InfalteToMaximum()
        {
            foreach (Wheel currentWheel in m_WheelsList)
            {
                currentWheel.inflateWheelToPSI(currentWheel.MaxPSI - currentWheel.CurrentPSI);
            }
        }

        public virtual void GetClassParametersNeededToCreate(ref List<string> io_ListOfParameters)
        {
            io_ListOfParameters.Add(k_ModelName);
            io_ListOfParameters.Add(k_EnergyPercentageLeft);
            m_WheelsList[0].GetClassParametersNeededToCreate(ref io_ListOfParameters);
        }

        public virtual void SetClassByParameters(ref Dictionary<string, string> io_Parameters)
        {
            if (GarageLogic.KeyFoundInDictionaryAndNotEmptyOrNull(io_Parameters, k_ModelName))
            {
                ModelName = io_Parameters[k_ModelName];
                io_Parameters[k_ModelName] = string.Empty;
            }

            if (GarageLogic.KeyFoundInDictionaryAndNotEmptyOrNull(io_Parameters, k_EnergyPercentageLeft))
            {
                EnergyPercentageLeft = io_Parameters[k_EnergyPercentageLeft];
                io_Parameters[k_EnergyPercentageLeft] = string.Empty;
                UpdateEngineByPercentage();
            }

            m_WheelsList[0].SetClassByParameters(ref io_Parameters);

            UpdateAllWheelsAccordingToTheFirstWheel();
        }

        public override int GetHashCode()
        {
            return r_LicenseNumber.GetHashCode();
        }

        /// <summary>
        /// A public method to check if 2 <see cref="object"/> is equal to the current <see cref="Vehicle"/>
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>The equality result</returns>
        public override bool Equals(object obj)
        {
            bool equals = false;
            Vehicle toCompareTo = obj as Vehicle;

            if (toCompareTo != null)
            {
                equals = this.GetHashCode() == toCompareTo.GetHashCode();
            }

            return equals;
        }

        public void UpdateEngineByPercentage()
        {
            r_Engine.UpdateCurrentCapacityByPercentage(m_EnergyPercentageLeft);
        }

        public void UpdatePercentageByEngineCurrentEnergy()
        {
            m_EnergyPercentageLeft = r_Engine.CalculatePercentageByCurrentEnergy();
        }

        public void FillWithFuel(eFuelType i_FuelType, float i_QuantityOfFuel)
        {
            FuelEngine fuel = Engine as FuelEngine;

            if (fuel == null)
            {
                throw new ArgumentException(k_ErrorNotRightTypeOfEngine);
            }
            else if (i_FuelType != fuel.FuelType)
            {
                throw new ArgumentException(string.Format(k_ErrorNotRightTypeOfFuel, fuel.FuelType.ToString()));
            }
            else
            {
                fuel.AddFuelSource(i_FuelType, i_QuantityOfFuel);
            }
        }

        public void ChargeEnergySource(float i_MinutsToCharge)
        {
            ElectricEngine electric = Engine as ElectricEngine;

            if (electric == null)
            {
                throw new ArgumentException(k_ErrorNotRightTypeOfEngine);
            }
            else
            {
                electric.AddBatterySource(i_MinutsToCharge);
            }
        }
        #endregion

        #region Private Methods
        private void UpdateAllWheelsAccordingToTheFirstWheel()
        {
            string manufacturerName = m_WheelsList[0].ManufacturerName;
            float currentPSI = m_WheelsList[0].CurrentPSI;

            foreach (Wheel wheel in m_WheelsList)
            {
                wheel.ManufacturerName = manufacturerName;
                wheel.CurrentPSI = currentPSI;
            }
        }
        #endregion
    }
}
